#include "map_file.h"
#include <stdio.h>

#ifdef DEBUG
	#define gprintf printf
#else
	#define gprintf(...)
#endif


std::vector< _map_file_entry > map_file;
int map_x = 1800;
int map_y = 1800;

char VerifyMap(const char *filename)
{
	int ret = 1;
	if(filename == NULL)
	{
		ret = -2;
		goto ret;
	}

	FILE* pMap = fopen(filename,"rb");
	if(pMap == NULL)
	{
		ret = -3;
		goto ret;
	}

	_map_file_header header;
	if ( read_file(pMap,&header,sizeof(_map_file_header)) != sizeof(_map_file_header))
	{
		ret = -4;
		goto ret;
	}

	if(header.sig != 0x44574d00) //DWM\0
	{
		ret = -5;
		goto ret;
	}

	if(header.map_x <= 0 && header.map_y <= 0)
	{
		ret = -6;
		goto ret;
	}

	//TODO : replace with realtime check that loads every entry and counts to see if its correct
	/*fseek (pMap , 0 , SEEK_END);
	int lSize = ftell (pMap);
	rewind (pMap);
	if(lSize !=  (header.entries * sizeof(_map_file_entry)) + sizeof(_map_file_header) )
		return -6;*/

	//its all fine :)
ret:
	if(pMap)
		fclose(pMap);
	return ret;
}
char ReadMap(std::string filename)
{
	if(map_file.size() > 0)
		map_file.clear();

	if(filename.c_str() == NULL)
		return -1;
	char ret = 1;
	ret = VerifyMap(filename.c_str());
	if(ret < 0)
		return ret;

	FILE* pMap = fopen(filename.c_str(),"rb");
	if(pMap == NULL)
		return -8;

	_map_file_header header;
	read_file(pMap,&header,sizeof(_map_file_header));
	map_x = header.map_x;
	map_y = header.map_y;
	
	for(unsigned int x = 0;x < header.entries;x++)
	{
		//read header & others
		gprintf("reading entry %d of %d\n\n\n",x+1,header.entries);
		_map_file_entry temp;
		memset(&temp,0,sizeof(_map_file_entry));
		int tet = read_file(pMap,&temp,MAP_SIZE_ENTRIES);
		//printf("fffffuuuu %d\n",tet);
		if ( tet != MAP_SIZE_ENTRIES)
		{
			ret = -9; //todo, check if we reached an error or EOF
			goto _return;
		}

		//read the texture name
		if(read_file(pMap,&temp.name_size,1) != 1)
		{
			ret = -10;
			goto _return;
		}
		char *temp_tex = (char*)malloc(sizeof(char)*temp.name_size);
		if(temp_tex == NULL)
		{
			ret = -11;
			goto _return;
		}
		memset(temp_tex,0,temp.name_size);
		if(temp.name_size != fread(temp_tex,1,temp.name_size,pMap))
		{
			ret = -12;
			free(temp_tex);
			temp_tex = NULL;
			goto _return;
		}
		temp.texture_name.assign(temp_tex,temp.name_size);
		free(temp_tex);
		temp_tex = NULL;

		//read description
		if(read_file(pMap,&temp.desc_size,1) != 1)
		{
			ret = -13;
			goto _return;
		}
		temp_tex = (char*)malloc(sizeof(char)*temp.desc_size);
		if(temp_tex == NULL)
		{
			ret = -14;
			goto _return;
		}
		memset(temp_tex,0,temp.desc_size);
		if(temp.desc_size != fread(temp_tex,1,temp.desc_size,pMap))
		{
			ret = -15;
			free(temp_tex);
			temp_tex = NULL;
			goto _return;
		}
		temp.entry_desc.assign(temp_tex,temp.desc_size);
		free(temp_tex);
		temp_tex = NULL;

		//read command & subcommand & type
		if(read_file(pMap,&temp.command,3) != 3)
		{
			ret = -16;
			goto _return;
		}
		else
			gprintf("command & subcommand : %x & %x & %x\n",temp.command,temp.subcommand,temp.type);

		//read the ending to verify file
		if(read_file(pMap,&temp.ending,4) != 4)
		{
			ret = -17;
			goto _return;
		}
		if(temp.ending == 0xdeadbeef)
			map_file.push_back(temp);
		else
			gprintf("deadbeef not found, found %x instead\n",temp.ending);
	}
_return:
	if(pMap)
		fclose(pMap);
	//if(ret >= 0)
		//return map_file.size();
	return ret;
}
char WriteMap(std::string filename)
{
	FILE* pMap = fopen(filename.c_str(),"wb");
	if(pMap == NULL)
		return -1;

	_map_file_header* header = new _map_file_header;
	header->map_x = map_x;
	header->map_y = map_y;
	header->entries = map_file.size();
	header->sig = 0x44574d00;
	//endian swap if needed. LE SIGH
	write_file(pMap,header,sizeof(_map_file_header));
	for(unsigned int x = 0;x < map_file.size();x++)
	{
		/*struct _map_file_entry
		{
			unsigned int x;
			unsigned int y;
			unsigned int w;
			unsigned int h;
			unsigned char name_size;
			std::string texture_name;
			unsigned char desc_size;
			std::string entry_desc;
			unsigned char command;
			unsigned char subcommand;
			unsigned char type;
			unsigned int ending;
		};*/
		char temp[255];
		memset(temp,0,255);
		strncpy(temp,map_file.at(x).texture_name.c_str(),map_file.at(x).name_size);
		write_file(pMap,&map_file[x],17); //first part writen
		fwrite(temp,1,map_file.at(x).name_size,pMap); //texture name written

		memset(temp,0,255);
		strncpy(temp,map_file.at(x).entry_desc.c_str(),map_file.at(x).desc_size);
		write_file(pMap,&map_file.at(x).desc_size,1);
		fwrite(temp,1,map_file.at(x).desc_size,pMap); //desc written

		write_file(pMap,&map_file.at(x).command,3); //the last part
		write_file(pMap,&map_file.at(x).ending,4);

	}	
	if(pMap)
		fclose(pMap);
	return 1;
}
size_t write_file( FILE* file, void* nbr ,int size )
{
	if(file == NULL || size == 0)
		return 0;
	if(!is_big_endian() )//&& size >= 4)
	{
		for(int x=0;x<=size;)
		{
			if(x+4 <= size)
			{
				unsigned int* pTmp = (unsigned int*)nbr;
				unsigned int temp = *(pTmp+(x/4));
				endian_swap(temp);
				//printf("%08x is now and will turn in ->%08x\n",*(pTmp+(x/4),temp);
				x+=4;
				fwrite(&temp,1,4,file); 
			}
			else if(x+2 <= size)
			{
				unsigned short* pTmp = (unsigned short*)nbr;
				//printf("%08x(0x%08x , %x) lolol\n",*(pTmp+x),pTmp+x,x);
				unsigned short temp = *(pTmp+(x/2));

				//NOTE : FOR SOME ODD REASON, fwrite swaps shorts. so ye, no need for a swap.
				//endian_swap(temp);
				//printf("%08x is now and will turn in ->%d",*(pTmp+x),temp);

				fwrite(&temp,1,2,file);
				x += 2;
			}
			else if(x+1 <= size)
			{
				unsigned char* pTmp = (unsigned char*)nbr;
				unsigned char temp = *(pTmp+x);
				//printf("writing %x, before was %x\n",temp,*(pTmp+x));
				fwrite(&temp,1,1,file);
				x++;
				if(x+1 >= size)
					break;
			}
			else
				break;
			
		}
		//fwrite(pTmp,1,size,file ); 
	}
   return 1;
}

size_t read_file( FILE* file, void* nbr ,int size )
{
	if(file == NULL || nbr == NULL || size == 0)
		return 0;
	size_t res = fread(nbr, 1, size , file );
	if(!is_big_endian() && size >= 4)
	{
		//printf("swapping content\n");
		std::vector<unsigned char> temp(size);
		memcpy(&temp[0],nbr,size);
		for(int x=0;x<size;)
		{
			if(size >= 4)
			{
				endian_swap(*(unsigned int*)&temp[x]);
				if(x+4 >= size)
					break;
				x = x+4;
			}
			else
			{
				endian_swap(*(unsigned short*)&temp[x]);
				if(x+2 >= size)
					break;
				x = x+2;
			}
			
		}
		memcpy(nbr,&temp[0],size);
	}
	return res;
}
int is_big_endian(void)
{
    union {
        uint32_t i;
        char c[4];
    } bint = {0x01020304};

    return bint.c[0] == 1; 
}
inline void endian_swap(unsigned short& x)
{
    x = (x>>8) | 
        (x<<8);
}

inline void endian_swap(unsigned int& x)
{
    x = (x>>24) | 
        ((x<<8) & 0x00FF0000) |
        ((x>>8) & 0x0000FF00) |
        (x<<24);
}
// __int64 for MSVC, "long long" for gcc
inline void endian_swap(unsigned __int64& x)
{
    x = (x>>56) | 
        ((x<<40) & 0x00FF000000000000) |
        ((x<<24) & 0x0000FF0000000000) |
        ((x<<8)  & 0x000000FF00000000) |
        ((x>>8)  & 0x00000000FF000000) |
        ((x>>24) & 0x0000000000FF0000) |
        ((x>>40) & 0x000000000000FF00) |
        (x<<56);
}
